/*
 *	CHANGE LOG
 * 
 * 01.02.05
 *
 *				   - DW - Updated the provider to match changes made in News Articles.
 * 
 *				   - DW - Added the ability to support default properties.
 *
 *		08/16/2008 - DW - Changed the ProcessSettingsFromCategories procedure to check for null on the 
 *					category string array passed in.
 *		08/28/2008 - DW - Added the ability to have email sent to article approvers when an article is 
 *					submitted for approval.
 *		10/02/2008 - DW - Updated the code to account for the new hierarchical categories feature
 *
 * 01.02.08
 * 
 *		11/03/2008 - DW - Updated the NewItem code to take care of an issue with the reflection when calling
 *						GetArticleLink.  Updated to work with the latest version of News Articles.
 *		11/04/2008 - DW - Changed the code in GetRecentItems to use Substring(PageText, 1, 2000) instead of
 *							just PageText which caused errors in SQL 2000.
 * 01.02.09
 * 
 *		11/09/2008 - DW - Updated the GetCategories method.  Replaced code which wasn't using reflection to call
 *							the ParentID property of the CategoryInfo object.  The code was there, it just hadn't been completed.
 * 
 *		11/11/2008 - DW - Updated the category code to add numbering in front of the categories.
 * 
 *		11/11/2008 - DW - Updated the GetItem code so that it doesn't add the article image if the image
 *							contains a FileId.  To update the article image, simply associate a new image with teh 
 *							article by adding a new image and setting the attribute to ai.
 * 01.02.10
 * 
 *		12/02/2008 - DW - Updated the code to account for changes Scott made to the GetCategoryList method.
 * 
 *		12/08/2008 - DW - Updated the GetRecentItems method to return all articles for ArticleApprovers
 * 
 * 01.02.11
 * 
 *		06/10/2008 - DW - Updated the code to support SmartSocial StoryFeed integration.
 *		
 * 01.02.12
 * 
 *      11/30/2009 - DW - Closed the data readers when connections are made to the db. 

 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Entities.Tabs;
using DotNetNuke.Entities.Users;
using Ventrian.NewsArticles;
using DataProvider = DotNetNuke.Data.DataProvider;
using DotNetNuke.Services.Localization;

namespace ITCrossing
{
    public class metaPostVentrianNewsArticleProvider : IPublishable
    {
        private bool _setAsEndDate = false;

        # region IPublishable Members

        # region Public Properties

        /// <summary>
        /// ProviderKey should be set to the Friendly Name of the module for
        /// which this provider is being implemented.
        /// </summary>
        public string ProviderKey
        {
            get
            {
                return MetaPostServices.GetFriendlyNameFromModuleDefinition("DnnForge - NewsArticles");
            }
        }

        /// <summary>
        /// ManifestPath is the path to the manifest file used to determine 
        /// the settings available in Windows Live Writer.  An example path might
        /// be /DesktopModules/itcMetaPost/manifests/wlwblog.xml.  See the following URL for 
        /// information regarding what Content can be placed in a WLW manifest file:
        /// http://msdn2.microsoft.com/en-us/library/bb463260.aspx
        /// </summary>
        public string ManifestFilePath
        {
            get { return "/DesktopModules/itcMetaPost/manifests/wlwnewsarticles.xml"; }
        }

        /// <summary>
        /// LocalizationFilePath is the path including the filename to the 
        /// resource file used by metaPost to retrieve the error messages
        /// shown to the user (eg. "/DesktopModules/itcMetaPost/App_LocalResources/itcMetaPost")
        /// </summary>
        public string LocalizationFilePath
        {
            get { return "/DesktopModules/itcMetaPost/App_LocalResources/itcMetaPostVentrianNews"; }
        }

        /// <summary>
        /// OPTIONAL - ImageUploadPath is a property metaPost uses to determine where 
        /// to store the media files (images).  This property should return just a 
        /// section of the path starting from the portal root.  Added to this path will
        /// be the username along with the path 
        /// </summary>
        public string ImageUploadPath
        {
            get
            {
                // This can be overridden with custom implementation if needed.  If
                // overridden, then a message should be placed in the ImagePath column
                // for your provider letting administrators know that administration of
                // the ImagePath is implemented elsewhere.  
                return MetaPostServices.GetProviderImageUploadPath(ProviderKey);
            }
        }

        /// <summary>
        /// AttachmentUploadPath is used to specify the location of attachments added
        /// to a post or page.
        /// </summary>
        public string AttachmentUploadPath
        {
            get
            {
                // For now, we'll just set this to the same as the image upload path
                return MetaPostServices.GetProviderAttachmentUploadPath(ProviderKey);
            }
        }

        /// <summary>
        /// SettingsUserControlPath is for future use.  It will return the path to the
        /// the settings user control for this provider.  
        /// </summary>
        public string SettingsUserControlPath
        {
            get { throw new NotImplementedException(); }
        }

        # endregion

        # region Get Methods

        /// <summary>
        /// The purpose of this procedure is to provide a list of all the modules in a 
        /// given portal to which the currently logged on user has either insert, update
        /// or delete access.  Note, this may not always correspond to an actual module.
        /// For example, with the DNN Blog module provider, a list of blogs is returned
        /// along with URL of where each blog is available within the site.  For the News
        /// Articles module, we return a list of all News Articles modules that have been
        /// added to Tabs throughout the site.
        /// </summary>
        /// <param name="userInfo"></param>
        /// <param name="portalSettings"></param>
        /// <returns></returns>
        /// <param name="providerKey"></param>
        public ModuleInfoStruct[] GetModulesForUser(UserInfo userInfo, PortalSettings portalSettings,
                                                    string providerKey)
        {
            ModuleInfoStruct[] infoArray = null;
            List<ModuleInfoStruct> moduleInfoList = new List<ModuleInfoStruct>();

            List<DnnModuleInfoStruct> dnnModuleInfos = MetaPostServices.GetModuleInfos(providerKey, portalSettings.PortalId);

            if (dnnModuleInfos.Count > 0)
            {
                infoArray = new ModuleInfoStruct[dnnModuleInfos.Count];
                for (int i = 0; i < dnnModuleInfos.Count; i++)
                {
                    // Check to see if the user has the right level of permission to post to this 
                    // module.
                    if (IsUserPrivileged(userInfo, "SubmissionRoles", dnnModuleInfos[i].ModuleId))
                    {
                        // Yes!  We have a role match, so user has auth priviledge
                        // for this particular News Article module.
                        ModuleInfoStruct moduleInfo = new ModuleInfoStruct();
                        ModuleController moduleController = new ModuleController();
                        ModuleInfo dnnModuleInfo =
                            moduleController.GetModule(dnnModuleInfos[i].ModuleId, dnnModuleInfos[i].TabId, false);
                        moduleInfo.ModuleLevelId = dnnModuleInfos[i].TabModuleId.ToString();
                        string modulePath = dnnModuleInfos[i].TabPath.Replace("//", "/");
                        // remove the beginning forward slash
                        if (modulePath.Length > 1 && modulePath.Substring(0, 1) == "/")
                            modulePath = modulePath.Substring(1);
                        moduleInfo.ModuleName = dnnModuleInfo.ModuleTitle + " (" + modulePath + ")";

                        moduleInfo.Url = MetaPostServices.GetRedirectUrl(providerKey, dnnModuleInfos[i].TabId);
                        moduleInfoList.Add(moduleInfo);
                        //break;
                    }
                }
                infoArray = moduleInfoList.ToArray();
            }
            else
            {
                throw new MetaPostException("NoModulesForUser",
                                            "There are no News Article modules associated with this user account.  Please enter a user account which has been associated with a blog.");
            }
            return infoArray;
        }
        /// <summary>
        /// GetRecentItems returns a list of the recent items added to the module.  For example, with
        /// the blog module, the list would return all the recent blog entries.  For the Text/HTML provider, 
        /// this method would return a list of the recently added Text/HTML modules in the site.
        /// </summary>
        /// <param name="moduleLevelId"></param>
        /// <param name="userInfo"></param>
        /// <param name="portalSettings"></param>
        /// <param name="numberOfItems"></param>
        /// <param name="requestType">GetRecentItems can be called for a number of reasons.  In Windows Live Writer,
        /// users are able to retrieve a list of recent posts or recent pages.  The requestType parameter is used
        /// to denote whether a list of pages or posts is being requested.  Additionally, WLW may request a list 
        /// of hiearchical pages to show pages when allowing a parent page to be assigned to a new page.  The values for this enum
        /// are RecentPosts, RecentPages and RecentPagesHiearchical.</param>
        /// <param name="providerKey"></param>
        /// <returns></returns>
        public Item[] GetRecentItems(string moduleLevelId, UserInfo userInfo, PortalSettings portalSettings, int numberOfItems, RecentItemsRequestType requestType, string providerKey)
        {
            string debugMessage = "Beginning";
            try
            {
                List<Item> itemArray = new List<Item>();

                int moduleId = Convert.ToInt32(moduleLevelId);

                debugMessage += ", step1";

                // Get the actual ModuleId since we're tracking the TabModuleId in the moduleLevelId parameter
                DnnModuleInfoStruct moduleInfo = MetaPostServices.GetModuleInfo(moduleId);
                int articleModuleId = moduleInfo.ModuleId;

                debugMessage += ", step2";

                ArticleController articleController = new ArticleController();
                //articleList = articleController.GetArticleList(articleModuleId, userInfo.UserID, false, true);
                ArrayList articleList = new ArrayList();

                //For admins and superuser accounts, we let them see all the entries except the one which have been 
                // submitted as draft.
                //int userId = userInfo.UserID;
                //if (userInfo.IsSuperUser || IsAdmin(userInfo.Roles))
                //{
                //    userId = Null.NullInteger;
                //}

                # region Old Approach which was subject to changes in the API

                //if (IsUserPrivileged(userInfo, "ApprovalRoles", moduleInfo.ModuleId))
                //{
                //    ArrayList articleListDraft;
                //    ArrayList articleListSubmitted;
                //    ArrayList articleListApproved;
                //    articleListApproved =
                //        articleController.GetArticleList(articleModuleId, null, Null.NullBoolean, Null.NullInteger,
                //                                         Null.NullInteger, true, false,
                //                                         Null.NullDate, null,
                //                                         Null.NullBoolean, false, userId, Null.NullBoolean,
                //                                         Null.NullBoolean, false, false, "CreatedDate", "DESC", Null.NullInteger,
                //                                         Null.NullInteger);
                //    articleListSubmitted =
                //        articleController.GetArticleList(articleModuleId, null, Null.NullBoolean, Null.NullInteger,
                //                                         Null.NullInteger, false, false,
                //                                         Null.NullDate, null,
                //                                         Null.NullBoolean, false, Null.NullInteger, Null.NullBoolean,
                //                                         Null.NullBoolean, false, false, "CreatedDate", "DESC", Null.NullInteger,
                //                                         Null.NullInteger);

                //    articleListDraft =
                //        articleController.GetArticleList(articleModuleId, null, Null.NullBoolean, Null.NullInteger,
                //                                         Null.NullInteger, false, true,
                //                                         Null.NullDate, null,
                //                                         Null.NullBoolean, false, userInfo.UserID, Null.NullBoolean,
                //                                         Null.NullBoolean, false, false, "CreatedDate", "DESC", Null.NullInteger,
                //                                         Null.NullInteger);
                //    articleList = new ArrayList();
                //    articleList.AddRange(articleListDraft);
                //    articleList.AddRange(articleListSubmitted);
                //    articleList.AddRange(articleListApproved);
                //}
                //else
                //{
                //    articleList =
                //        articleController.GetArticleList(articleModuleId, null, Null.NullBoolean, Null.NullInteger,
                //                                         Null.NullInteger, Null.NullBoolean, true,
                //                                         Null.NullDate, null,
                //                                         Null.NullBoolean, false, userInfo.UserID, Null.NullBoolean,
                //                                         Null.NullBoolean, false, false, "CreatedDate", "DESC", Null.NullInteger,
                //                                         Null.NullInteger);
                //}

                # endregion

                debugMessage += ", step3";

                string addWhere = string.Empty;
                string userWhere = string.Empty;
                bool isAutoApproval = IsUserPrivileged(userInfo, "AutoApprovalRoles", moduleInfo.ModuleId);
                bool isApproval = IsUserPrivileged(userInfo, "ApprovalRoles", moduleInfo.ModuleId);
                if (!isAutoApproval && !isApproval)
                {
                    // User is not privileged, so we'll only show the drafts
                    addWhere = " AND a.IsDraft = 1 ";
                }

                if (!isApproval)
                {
                    // User is not in ApprovalRole, so we'll only show them their articles
                    userWhere = " AND a.AuthorId = " + userInfo.UserID;
                }
                else
                {
                    userWhere = "  AND ((a.IsDraft = 0 AND a.AuthorId <> " + userInfo.UserID +
                                ") OR (a.AuthorId = " +
                                userInfo.UserID + "))";
                }

                debugMessage += ", step4";

                string SQL = "SELECT a.ArticleId, a.URL, [Summary] = IsNull(a.Summary, (SELECT Substring(PageText, 1, 2000) FROM " +
                             MetaPostServices.DatabaseOwner + MetaPostServices.ObjectQualifier +
                             "DnnForge_NewsArticles_Page p (nolock) WHERE ArticleId = a.ArticleId AND PageID = (SELECT Min(PageId) FROM " +
                             MetaPostServices.DatabaseOwner + MetaPostServices.ObjectQualifier +
                             "DnnForge_NewsArticles_Page (nolock) WHERE ArticleId = a.ArticleID))), a.Title, a.CreatedDate FROM " +
                             MetaPostServices.DatabaseOwner + MetaPostServices.ObjectQualifier +
                             "DnnForge_NewsArticles_Article a (nolock) WHERE a.ModuleId = " + articleModuleId +
                             userWhere +
                             addWhere +
                             " ORDER BY a.CreatedDate DESC";

                debugMessage += ", step5";

                using (IDataReader dr = DataProvider.Instance().ExecuteSQL(SQL))
                {

                    debugMessage += ", step6";

                    while (dr.Read())
                    {
                        try
                        {
                            ArticleInfo ai = new ArticleInfo();
                            // Check first to make sure there's a URL
                            if (dr.IsDBNull(dr.GetOrdinal("URL")))
                            {
                                ai.Url = " ";
                            }
                            else
                            {
                                ai.Url = dr["URL"].ToString();
                            }
                            ai.ArticleText = HttpUtility.HtmlDecode(dr["Summary"].ToString());
                            ai.Title = dr["Title"].ToString();
                            ai.CreatedDate = (DateTime)dr["CreatedDate"];
                            ai.ArticleID = (int)dr["ArticleId"];
                            articleList.Add(ai);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception(
                                "There was an error while looping through the list of articles returned from the database.  Here is the SQL that was executed: " +
                                SQL);
                        }
                    }
                }

                debugMessage += ", step7";

                // Find which is the least, numberOfPosts or arEntries.Count
                int loopCutOff = (numberOfItems == -1 || numberOfItems >= articleList.Count)
                                    ? articleList.Count
                                    : numberOfItems;

                //itemArray = new Item[loopCutOff];

                debugMessage += ", step8";

                int i = 0;
                foreach (ArticleInfo entry in articleList)
                {
                    Item item = new Item();
                    item.Link = entry.Url;
                    item.Content = HttpUtility.HtmlDecode(entry.ArticleText ?? " ");
                    item.DateCreated = entry.CreatedDate;

                    debugMessage += ", step9";

                    item.Title = entry.Title;
                    item.Permalink = entry.Url;

                    debugMessage += ", step10";

                    // If requestType is for recent pages
                    if (requestType == RecentItemsRequestType.RecentPages
                        ) //|| requestType == RecentItemsRequestType.RecentPosts
                    {
                        PageController pageController = new PageController();
                        ArrayList pageList = pageController.GetPageList(entry.ArticleID);
                        Item pageWithLowestSortOrder = new Item();
                        int lowestSortOrder = int.MaxValue;
                        foreach (PageInfo pageInfo in pageList)
                        {
                            Item pageItem = new Item();
                            pageItem.Link = entry.Url;
                            pageItem.Content = HttpUtility.HtmlDecode(pageInfo.PageText ?? " ");
                            pageItem.Summary = HttpUtility.HtmlDecode(entry.Summary ?? " ");
                            pageItem.DateCreated = entry.CreatedDate;
                            pageItem.ItemId = pageInfo.PageID.ToString();
                            pageItem.Title = (requestType == RecentItemsRequestType.RecentPages &&
                                              entry.Title != pageInfo.Title)
                                                ? entry.Title + " :: " + pageInfo.Title
                                                : entry.Title;
                            pageItem.Permalink = entry.Url;
                            pageItem.PageOrder = pageInfo.SortOrder.ToString();
                            pageItem.ParentId = "aid" + pageInfo.ArticleID.ToString();
                            // Only add all pages if the requestType is for RecentPages
                            if (requestType == RecentItemsRequestType.RecentPages)
                                itemArray.Add(pageItem);
                            // If the requestType is for recentPosts, then we want to determine
                            // which page has the lowest SortOrder.
                            if (requestType == RecentItemsRequestType.RecentPosts)
                            {
                                if (pageInfo.SortOrder < lowestSortOrder)
                                {
                                    pageWithLowestSortOrder = pageItem;
                                    lowestSortOrder = pageInfo.SortOrder;
                                }
                            }
                        }
                        // For requestType of RecentPosts, we only want to add the lowest sortorder page.
                        // we also want to check to make sure at least one page was loaded for this article.
                        if (requestType == RecentItemsRequestType.RecentPosts && lowestSortOrder != int.MaxValue)
                            itemArray.Add(pageWithLowestSortOrder);
                    }
                    else
                    {
                        item.ItemId = "aid" + entry.ArticleID.ToString();
                        itemArray.Add(item);
                    }
                    i++;
                    if (i >= loopCutOff) break;
                }

                debugMessage += ", step11";

                return itemArray.ToArray();

            }
            catch (Exception ex)
            {
                throw new Exception("Method Progress:" + debugMessage);
            }
        }

        /// <summary>
        /// GetItem is used to retrieve a specific item, which may be a page or a post if your module supports
        /// both concepts.  In cases where a module supports both pages and posts, a concept made available in 
        /// Windows Live Writer, but not in Word 2007 or in many other publishing clients, the itemType parameter 
        /// can be used to distinguish between when this method is being called to add a page or a post.  If your 
        /// provider doesn't support a distinction between pages and posts, then simply ignore the itemType parameter.
        /// </summary>
        /// <param name="itemId"></param>
        /// <param name="userInfo"></param>
        /// <param name="portalSettings"></param>
        /// <param name="itemType"></param>
        /// <returns></returns>
        public Item GetItem(string itemId, UserInfo userInfo, PortalSettings portalSettings, ItemType itemType)
        {
            Item item = new Item();
            int pageId = -1;
            int articleId = -1;
            string imageUrl = string.Empty;
            PageInfo pageInfo = null;

            // Get our controllers ready.
            ArticleController articleController = new ArticleController();
            PageController pageController = new PageController();

            # region "Get ArticleId and pageInfo object"
            if (itemId.Length > 3 && itemId.Substring(0, 3) == "aid")
            {
                // ARTICLE
                articleId = Convert.ToInt32(itemId.Substring(3));

                ArrayList pageList = pageController.GetPageList(articleId);
                int idOfLowestSortOrder = int.MaxValue;
                foreach (PageInfo pageInfoTemp in pageList)
                {
                    if (pageInfoTemp.SortOrder < idOfLowestSortOrder)
                    {
                        pageInfo = pageInfoTemp;
                        idOfLowestSortOrder = pageInfoTemp.SortOrder;
                    }
                }

            }
            else
            {
                // PAGE
                pageId = Convert.ToInt32(itemId);
                pageInfo = pageController.GetPage(pageId);
                articleId = pageInfo.ArticleID;
            }
            # endregion

            ArticleInfo articleInfo = articleController.GetArticle(articleId);

            if (articleInfo == null)
            {
                throw new MetaPostException("NoPostAvailable",
                                            "There was en error retrieving the blog entry.  Try closing and restarting the software you're using to edit your blog post.");
            }

            item.Link = articleInfo.Url;

            if (pageInfo != null)
            {
                item.Content = HttpUtility.HtmlDecode(pageInfo.PageText);
                item.Title = pageInfo.Title;
                item.PageOrder = pageInfo.SortOrder.ToString();
            }

            if (itemType == ItemType.Page)
            {
                item.ItemId = pageId.ToString();
            }
            else
            {
                //item.Content = HttpUtility.HtmlDecode(articleInfo.Summary);
                item.ItemId = "aid" + articleId.ToString();
                item.Title = articleInfo.Title;
            }
            item.DateCreated = articleInfo.StartDate;
            item.Permalink = articleInfo.Url;
            item.StartDate = articleInfo.StartDate;
            imageUrl = articleInfo.ImageUrl;

            // The following was added to workaround an issue with WLW where the 
            // ParentId is shown rather than the parent name in the dropdown list
            // of possible parents.  So the user isn't confused by seeing an Id
            // rather than the parent name, we'll send the name which will cause
            // WLW to add the name to the list.  In the EditItem procedure, we'll 
            // ignore this name added to the list.
            if (itemType == ItemType.Page)
                item.ParentId = articleInfo.Title;
            else
                item.ParentId = "aid" + articleId.ToString();

            item.Summary = HttpUtility.HtmlDecode(articleInfo.Summary);

            # region Old Category Code
            //// Get the Categories for the article
            //ArrayList cats = articleController.GetArticleCategories(articleInfo.ArticleID);
            //ItemCategoryInfo[] moduleCategories = GetCategories(articleInfo.ModuleID);
            //List<string> catValues = new List<string>();

            //foreach (CategoryInfo categoryInfo in cats)
            //{
            //    // We're looping through the categories for the article and for each one
            //    // we'll need to find the corresponding category in the overall list of categories.
            //    // This will ensure that any numbering or formatting added to the categories
            //    // is reflected in the categories we add to the item.
            //    foreach (ItemCategoryInfo ci in moduleCategories)
            //    {
            //        if (ci.CategoryId == categoryInfo.CategoryID)
            //        {
            //            catValues.Add(categoryInfo.Name);
            //        }
            //    }
            //}

            //// Check to see if any of the settings categories need to be added:
            //if (articleInfo.IsFeatured)
            //{
            //    catValues.Add(MetaPostServices.GetString("Featured", "Featured", LocalizationFilePath));
            //}
            //if (articleInfo.IsSecure)
            //{
            //    catValues.Add(MetaPostServices.GetString("Secure", "Secure", LocalizationFilePath));
            //}
            # endregion

            ItemCategory[] articleCategories =
                GetItemCategories("aid" + articleInfo.ArticleID.ToString(), userInfo, portalSettings);
            List<string> catValues = new List<string>();
            foreach (ItemCategory ic in articleCategories)
            {
                catValues.Add(ic.CategoryName);
            }

            item.Categories = catValues.ToArray();

            if (itemType == ItemType.Post)
                FindAndPlaceSummary(ref item);

            ShowFeaturedImage(ref item, imageUrl);

            return item;
        }

        # endregion

        # region Add, Edit and Delete Methods

        /// <summary>
        /// NewItem is used for creating new items (either pages or posts).  The implementation 
        /// in your provider can distinguish between page and post requests for NewItem by the 
        /// ItemType property of the Item struct.  Additional properties of the Item Struct which 
        /// may be of interest in the implmentation of this method are: Publish, StyleDetectionPost,StyleId, and ItemType.
        /// See the comments associated with each of these properties of the Item struct for more information.
        /// </summary>
        /// <param name="moduleLevelId">String - ModuleId is a very loose concept here.  Anything can be tracked in this variable as long as it is consistent.  For the Blog module, the BlogId is passed and for the Text/HTML module, the ModuleId is passed and for the Ventrian News Articles module, the ArticleId is passed.  This value is based on the value sent to Windows Live Writer when GetModulesForUser is called. </param>
        /// <param name="userInfo">DotNetNuke UserInfo object.  This UserInfo object is retrieved by metaPost and made available to every method call in the MetaPostProvider interface.</param>
        /// <param name="portalSettings">DotNetNuke PortaSettings object - This is provided to make it easier to work with metaPost.  metaPost does all the work behind the scenes of identifying the right portalSettings object based on the requested URL.</param>
        /// <param name="item">Custom Struct - The item struct contains a list of fields sent through the MetaWeblog API.  The properties have been renamed to make sense in more general applications outside of just blogs.</param>
        /// <returns></returns>
        public string NewItem(string moduleLevelId, UserInfo userInfo, PortalSettings portalSettings, Item item)
        {
            List<string> images = MetaPostServices.GetImageListFromItem(ref item);
            int articleId = -1;
            int pageId = -1;
            string returnId = string.Empty;
            string featuredImage = string.Empty;

            ExtractSummaryFromExtendedContent(ref item);

            // Now that we know the content is either in item.Summary or item.Content, we 
            // try to extract the image from the content
            featuredImage = GetFeaturedImage(ref item);

            if (item.ItemType == ItemType.Page)
            {
                // Check to see if a parent has been selected for the page.  If not, then
                // throw an exception to let the user know they need to select a parent.
                if (string.IsNullOrEmpty(item.ParentId) || item.ParentId == "0")
                {
                    throw new MetaPostException("PageParentRequired", "A page parent is required.  In Windows Live Writer, the Page Parent dropdown appears in the bottom left part of the screen.");
                }
                articleId = Convert.ToInt32(item.ParentId.Substring(3));
            }
            ArticleInfo articleInfo;
            // Get the actual ModuleId since we're tracking the TabModuleId in the moduleLevelId parameter
            DnnModuleInfoStruct moduleInfo = MetaPostServices.GetModuleInfo(Convert.ToInt32(moduleLevelId));
            int articleModuleId = moduleInfo.ModuleId;

            ArticleController articleController = new ArticleController();
            if (item.ItemType == ItemType.Page)
            {
                articleInfo = articleController.GetArticle(articleId);
                if (!string.IsNullOrEmpty(item.Summary) && !string.IsNullOrEmpty(item.Content))
                    articleInfo.Summary = HttpUtility.HtmlEncode(item.Summary);
            }
            else
            {
                articleInfo = new ArticleInfo();
                articleInfo.Title = item.Title;
                articleInfo.ArticleText = HttpUtility.HtmlEncode(item.Content);
                articleInfo.CreatedDate = DateTime.Now;
                articleInfo.AuthorID = userInfo.UserID;
                articleInfo.Summary = HttpUtility.HtmlEncode(item.Summary);

                ProcessSettingsFromCategories(item.Categories, ref articleInfo, portalSettings);

            }

            # region Code for Detecting the Site Template
            if (item.StyleDetectionPost)
            {
                articleInfo.Summary = item.Summary;
                //"<p>This is a temporary post that was not deleted. Please delete this manually. (7cbfe02f-ccb5-48d1-a96a-873789a4bc4e - 3bfe001a-32de-4114-a6b4-4005b770f6d7)</p>";
                articleInfo.ApproverID = userInfo.UserID;
                articleInfo.Status = StatusType.Published;
                articleInfo.IsDraft = false;
            }
            # endregion

            ModuleController moduleController = new ModuleController();
            ModuleInfo dnnModuleInfo = moduleController.GetModule(moduleInfo.ModuleId, moduleInfo.TabId, false);
            Hashtable settings = PortalSettings.GetTabModuleSettings(new Hashtable(PortalSettings.GetModuleSettings(moduleInfo.ModuleId)), new Hashtable(PortalSettings.GetTabModuleSettings(Convert.ToInt32(moduleLevelId))));
            ArticleSettings articleSettings = new ArticleSettings(settings, portalSettings, dnnModuleInfo);
            DateTime adjustedPubTime;
            if (item.DateCreated.Year > 1)
                adjustedPubTime = Common.GetAdjustedServerTime(articleSettings, item.DateCreated.AddMinutes(-userInfo.Profile.TimeZone + portalSettings.TimeZoneOffset - articleSettings.ServerTimeZone));
            else
                adjustedPubTime = item.DateCreated;
            DateTime adjustedServerTime = Common.GetAdjustedServerTime(articleSettings, DateTime.Now.AddMinutes(portalSettings.TimeZoneOffset - articleSettings.ServerTimeZone)); //.ToUniversalTime().AddMinutes(userInfo.Profile.TimeZone)

            // Set the publish date if set in WLW.
            if (adjustedPubTime.Year > 1)
            {

                if (_setAsEndDate)
                {
                    articleInfo.StartDate = adjustedServerTime;
                    articleInfo.EndDate = adjustedPubTime;
                }
                else
                {
                    articleInfo.StartDate = adjustedPubTime;
                    articleInfo.EndDate = Null.NullDate;
                }
            }
            else
            {
                articleInfo.StartDate = adjustedServerTime;
                articleInfo.EndDate = Null.NullDate;
            }

            articleInfo.ModuleID = articleModuleId;
            articleInfo.IsDraft = !item.Publish;
            articleInfo.LastUpdate = DateTime.Now;
            articleInfo.LastUpdateID = userInfo.UserID;
            articleInfo.ImageUrl = featuredImage;

            // Check to see if this is an approver and they're also trying to publish.  If so, then
            // just publish it.
            if (IsUserPrivileged(userInfo, "AutoApprovalRoles", articleModuleId) && item.Publish)
            {
                articleInfo.ApproverID = userInfo.UserID;
                articleInfo.IsApproved = true;
            }

            //

            if (item.ItemType == ItemType.Post)
            {
                articleId = articleController.AddArticle(articleInfo);
            }
            else
            {
                // Update the parent article if any changes were made
                articleController.UpdateArticle(articleInfo);
            }

            # region Code for Detecting the Site Template
            if (item.StyleDetectionPost)
            {
                // Retrieve the URL for the article and set it in the Url field
                // This will just be deleted in a minute, so it doesn't matter.
                if (item.ItemType == ItemType.Post)
                {
                    // Retrieve the articleInfo from the DB just to make sure we have
                    // all the updated values.
                    articleInfo = articleController.GetArticle(articleId);

                    Type articleControllerType = articleController.GetType();
                    Common common = new Common();
                    Type commonType = common.GetType();

                    TabController tabController = new TabController();
                    TabInfo tabInfo = tabController.GetTab(moduleInfo.TabId, portalSettings.PortalId, false);

                    // 04/29/2008 - Scott removed a parameter.  We'll use reflection
                    // to see if this method has 3 or 4 parameters.  Only the latest version 
                    // supports 3 parameters


                    MethodInfo miGetArticleLink = null;

                    miGetArticleLink = articleControllerType.GetMethod("GetArticleLink");
                    if (miGetArticleLink == null)
                    {
                        // Scott moved GetArticleLink out of the articleController and into Common as 
                        // of 6.06
                        miGetArticleLink =
                            commonType.GetMethod("GetArticleLink",
                                                 new Type[] { typeof(ArticleInfo), typeof(TabInfo), typeof(bool) });
                        if (miGetArticleLink == null)
                        {
                            // Signature changed again.  We'll try to get a reference a different way.
                            MethodInfo[] methodInfos = commonType.GetMethods();

                            foreach (MethodInfo mi in methodInfos)
                            {
                                if (mi.Name == "GetArticleLink")
                                {
                                    miGetArticleLink = mi;
                                    break;
                                }
                            }
                        }
                    }

                    string tempInstallUrl = null;
                    if (miGetArticleLink != null)
                    {
                        ParameterInfo[] piParameters = miGetArticleLink.GetParameters();
                        if (piParameters.Length == 3)
                        {
                            object[] methodParams = new object[3];
                            methodParams.SetValue(articleInfo, 0);
                            methodParams.SetValue(tabInfo, 1);
                            methodParams.SetValue(false, 2);
                            tempInstallUrl = (string)miGetArticleLink.Invoke(articleControllerType, methodParams);
                        }
                        else if (piParameters[2].ParameterType == typeof(bool))
                        {
                            object[] methodParams = new object[4];
                            methodParams.SetValue(articleInfo, 0);
                            methodParams.SetValue(tabInfo, 1);
                            methodParams.SetValue(false, 2);
                            methodParams.SetValue(0, 3);
                            tempInstallUrl = (string)miGetArticleLink.Invoke(articleControllerType, methodParams);
                        }
                        else if (piParameters[3].ParameterType == typeof(bool))
                        {
                            object[] methodParams = new object[4];
                            methodParams.SetValue(articleInfo, 0);
                            methodParams.SetValue(tabInfo, 1);
                            methodParams.SetValue(articleSettings, 2);
                            methodParams.SetValue(false, 3);
                            tempInstallUrl = (string)miGetArticleLink.Invoke(articleControllerType, methodParams);
                        }

                    }
                    else
                    {
                        tempInstallUrl =
                            DotNetNuke.Common.Globals.NavigateURL(tabInfo.TabID, string.Empty,
                                                                  new string[]
							                                      	{
							                                      		"articletype=articleview",
							                                      		"articleid=" + articleInfo.ArticleID
							                                      	});
                    }
                    if (tempInstallUrl != null) MetaPostServices.SetTempInstallUrl(ProviderKey, tempInstallUrl);
                }
            }
            # endregion

            // Create the new page if there's content in the page.  Don't create the 
            // page if there isn't any content.
            if (!string.IsNullOrEmpty(item.Content) || item.ItemType == ItemType.Page)
            {
                PageController pageController = new PageController();
                PageInfo pageInfo = new PageInfo();
                pageInfo.Title = item.Title;
                pageInfo.PageText = (string.IsNullOrEmpty(item.Content))
                    ? HttpUtility.HtmlEncode(item.Summary)
                    : HttpUtility.HtmlEncode(item.Content);
                //TODO - Remove RemovePageFooterIfRequested(ref pageInfo);

                int pageOrder = GetNextPageOrder(item.PageOrder, articleId);

                pageInfo.SortOrder = Convert.ToInt32(pageOrder);
                pageInfo.ArticleID = Convert.ToInt32(articleId);

                pageId = pageController.AddPage(pageInfo);
                returnId = pageId.ToString();
            }
            else
            {
                returnId = "aid" + articleId;
            }

            // Only save the Categories and send email messages if this is a new post
            if (item.ItemType == ItemType.Post)
            {
                // next, we load a list of categories that should be excluded (Settings
                // and categorization categories.  
                List<string> categoriesToExclude = GetCategoriesToExclude(portalSettings);

                // Load the default categories

                string defaultCategoryIds = settings[ArticleConstants.DEFAULT_CATEGORIES_SETTING] as string;

                List<int> categoriesToAdd = new List<int>();
                if (defaultCategoryIds != null)
                {
                    string[] defaultCatIdArray = defaultCategoryIds.Split(',');
                    foreach (string catId in defaultCatIdArray)
                    {
                        try
                        {
                            categoriesToAdd.Add(Convert.ToInt32(catId));
                        }
                        catch { }
                    }
                }

                // Add the Categories if any were selected
                if ((item.Categories != null && item.Categories.Length > 0) || categoriesToAdd.Count > 0)
                {
                    // Load the list of Categories for this module
                    CategoryController categoryContoller = new CategoryController();

                    //MethodInfo mi = categoryContoller.GetType().GetMethod("GetCategoryListAll", new Type[] {typeof(int),typeof(int)});
                    //object[] methodParams = null;
                    //if (mi != null)
                    //{
                    //    methodParams = new object[2];
                    //    methodParams.SetValue(articleModuleId, 0);
                    //    methodParams.SetValue(-1, 1);
                    //}
                    //else
                    //{
                    //    mi = categoryContoller.GetType().GetMethod("GetCategoryList");
                    //    methodParams = new object[1];
                    //    methodParams.SetValue(articleModuleId, 0);
                    //}

                    //ArrayList moduleCategoriesList = (ArrayList)mi.Invoke(categoryContoller, methodParams);
                    ItemCategoryInfo[] moduleCategoriesList = GetCategories(articleModuleId, portalSettings);

                    if (item.Categories != null && item.Categories.Length > 0)
                    {
                        foreach (string category in item.Categories)
                        {
                            // Only execute if this isn't one of the exclude categories
                            if (!categoriesToExclude.Contains(category))
                            {
                                // Loop through the category names to get the categoryid for adding to the article.
                                foreach (ItemCategoryInfo categoryInfo in moduleCategoriesList)
                                {
                                    if (categoryInfo.CategoryName == category && !categoriesToAdd.Contains(categoryInfo.CategoryId))
                                    {
                                        categoriesToAdd.Add(categoryInfo.CategoryId);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    // Now we loop through and add the categories
                    foreach (int catId in categoriesToAdd)
                    {
                        articleController.AddArticleCategory(articleId, catId);
                    }
                }

                // Check to see if we need to send email messages
                // We only send these if the post is marked for publish and the author does not have 
                // auto approval privileges.
                if (!item.StyleDetectionPost
                    && item.Publish)
                {
                    if (!IsUserPrivileged(userInfo, "AutoApprovalRoles", articleModuleId))
                    {
                        SendSubmittedForApprovalEmail(settings, articleSettings, moduleInfo.ModuleId, articleId,
                                                      moduleInfo.TabId, portalSettings);
                    }
                    else
                    {
                        TabController tabController = new TabController();
                        int tabId = GetTabId(moduleInfo.ModuleId, portalSettings);
                        TabInfo tabInfo = tabController.GetTab(tabId, portalSettings.PortalId, false);
                        articleInfo.ArticleID = articleId;
                        SetStoryFeed(articleSettings, articleInfo, tabInfo, portalSettings, userInfo);
                    }

                }
            }

            MetaPostServices.SaveItemImages(articleId, images, ProviderKey, portalSettings, userInfo.UserID);

            // Call a stored procedure which saves the FileId for the Featured Image
            if (!string.IsNullOrEmpty(featuredImage))
            {
                string SQL = "itcmetapost_news_articles_update_imageurl";
                DataProvider.Instance().ExecuteNonQuery(SQL, new object[] { articleId });
            }

            return returnId;
        }

        private void SetStoryFeed(ArticleSettings articleSettings, ArticleInfo article, TabInfo tabInfo, PortalSettings portalSettings, UserInfo userInfo)
        {
            PropertyInfo pi = articleSettings.GetType().GetProperty("EnableSmartThinkerStoryFeed");
            if (pi != null)
            {
                bool enableStoryFeed = (bool)pi.GetValue(articleSettings, null);
                if ((enableStoryFeed))
                {
                    wsStoryFeed.StoryFeedWS objStoryFeed = new wsStoryFeed.StoryFeedWS();
                    objStoryFeed.Url = HttpContext.Current.Request.Url.Scheme + "://" + portalSettings.PortalAlias.HTTPAlias + "/"
                        + MetaPostServices.GetAppPath() + "DesktopModules/Smart-Thinker%20-%20UserProfile/StoryFeed.asmx";

                    string val = GetSharedResource("StoryFeed-AddArticle");

                    val = val.Replace("[AUTHOR]", userInfo.DisplayName);
                    val = val.Replace("[AUTHORID]", userInfo.UserID.ToString());
                    val =
                        val.Replace("[ARTICLELINK]",
                                    Common.GetArticleLink(article, tabInfo, articleSettings,
                                                          false));
                    val = val.Replace("[ARTICLETITLE]", article.Title);

                    try
                    {
                        objStoryFeed.AddAction(80, article.ArticleID, val, userInfo.UserID,
                                               "VE6457624576460436531768");
                    }
                    catch
                    {
                    }
                }
            }
        }

        public string GetSharedResource(string key)
        {

            string path = "~/DesktopModules/DnnForge - NewsArticles/" + Localization.LocalResourceDirectory + "/" + Localization.LocalSharedResourceFile;
            return DotNetNuke.Services.Localization.Localization.GetString(key, path);

        }

        /// <summary>
        /// EditItem is called to edit either a page or a post.  The difference
        /// between the two is denoted in a property of the Item struct (ItemType).
        /// An additional property of the Item struct that may be important to the 
        /// implementation of the EditItem method is the Publish property.  This is 
        /// set to true when the item should be published.  The default setting of 
        /// false denotes that the item may be saved as a draft.
        /// </summary>
        /// <param name="moduleLevelId"></param>
        /// <param name="userInfo"></param>
        /// <param name="portalSettings"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool EditItem(string moduleLevelId, UserInfo userInfo, PortalSettings portalSettings, Item item)
        {
            List<string> images = MetaPostServices.GetImageListFromItem(ref item);

            int pageId = -1;
            int articleId = -1;
            PageInfo pageInfo = null;
            string featuredImage = string.Empty;
            Hashtable settings = new Hashtable();
            ArticleSettings articleSettings = null;

            // Get our controllers ready.
            ArticleController articleController = new ArticleController();
            PageController pageController = new PageController();
            bool newPageNeeded = false;

            # region Retrieve the PageId and ArticleId

            // We're dealing with an article and not a page
            if (((string)item.ItemId).Length > 3 && ((string)item.ItemId).Substring(0, 3) == "aid")
            {
                articleId = Convert.ToInt32(((string)item.ItemId).Substring(3));
                // If we're editing an article, we have two scenarios for the article:
                //		-There is a summary and content
                //		-There is only a summary
                // If it's the second case, then we don't try to update a page
                // If it's the first case, then we must determine if this is new content
                // or if we should update the first page of the article.  We'll make this 
                // determination based on whether or not pages exist.
                ArrayList pageList = pageController.GetPageList(articleId);
                int idOfLowestSortOrder = int.MaxValue;
                foreach (PageInfo pageInfoTemp in pageList)
                {
                    if (pageInfoTemp.SortOrder < idOfLowestSortOrder)
                    {
                        pageInfo = pageInfoTemp;
                        idOfLowestSortOrder = pageInfoTemp.SortOrder;
                    }
                }
                // If there is no page associated with the article yet and 
                //		-There is an extended content OR
                //		-There is both a summary and content
                // Then we want to create a new page associated with the article.
                // DW - 06/17/2008 - Changed to accomodate changes Scott made to NA as of 00.06.07
                if (IsPrePageRequired())
                {
                    // Version of NA < 00.06.07
                    if (pageInfo == null &&
                        (!string.IsNullOrEmpty(item.ExtendedContent)
                         || (!string.IsNullOrEmpty(item.Summary) && !string.IsNullOrEmpty(item.Content))))
                    {
                        newPageNeeded = true;
                        pageInfo = new PageInfo();
                    }
                }
                else
                {
                    // Version of NA >= 00.06.07
                    if (pageInfo == null)
                    {
                        // The pageInfo should not be null since with NA after 00.06.07 each article 
                        // should have a page.  This code is needed in case the client has edited the 
                        // content in the database or in case of articles edited with metaPost prior 
                        // to this fix.
                        newPageNeeded = true;
                        pageInfo = new PageInfo();
                    }
                }
                item.ParentId = "aid" + articleId;
            }
            else // We're dealing with a page
            {
                pageId = Convert.ToInt32(item.ItemId);
                pageInfo = pageController.GetPage(pageId);
                articleId = pageInfo.ArticleID;
            }

            # endregion

            if (item.ItemType == ItemType.Post) ExtractSummaryFromExtendedContent(ref item);

            // Now that we know the content is either in item.Summary or item.Content, we 
            // try to extract the image from the content
            featuredImage = GetFeaturedImage(ref item);

            # region Edit the Page
            if (pageInfo != null)
            {
                pageInfo.Title = item.Title;
                pageInfo.PageText = HttpUtility.HtmlEncode(item.Content);
                if (item.PageOrder != null)
                {
                    pageInfo.SortOrder = Convert.ToInt32(Regex.Replace(item.PageOrder, "[^0-9]", ""));
                }
                // Note that we have to include this code to work around a bug in WLW.  WLW
                // will show the id of the parent in the interface instead of the name.  To 
                // work around this, we return the name as the id which causes WLW to add an 
                // entry to the parent page list.  However, this entry is just there to keep
                // from confusing the user.  We'll check to see if the value in ParentId
                // is numeric.  If it isn't, then we know the user didn't change the ParentId.
                int parentId = 0;
                if (item.ParentId != null
                    && item.ParentId.Length > 3
                    && int.TryParse(item.ParentId.Substring(3), out parentId))

                    if (parentId != 0) pageInfo.ArticleID = parentId; // Convert.ToInt32(item.ParentId);

                //TODO - Remove RemovePageFooterIfRequested(ref pageInfo);

                if (newPageNeeded)
                {
                    pageController.AddPage(pageInfo);
                }
                else
                {
                    pageController.UpdatePage(pageInfo);
                }
            }
            # endregion

            # region Edit the Article

            ArticleInfo articleInfo = articleController.GetArticle(articleId);

            // Check to make sure that the articles' status is still Draft.
            // If it's not, then we raise an XmlRpcFaultException to let the 
            // user know that they can't edit the post now that it has been
            // submitted for approval.
            if (articleInfo.Status != StatusType.Draft
                && !IsUserPrivileged(userInfo, "AutoApprovalRoles", articleInfo.ModuleID))
                throw new MetaPostException("NoDraftEditAttempt",
                    "This article has either already been submitted for approval or is already approved and can not currently be edited.");

            // Only set the article properties if this is a post.  Page edits should
            // only edit the page properties and not the article.
            if (item.ItemType == ItemType.Post)
            {
                articleInfo.Title = item.Title;
                //articleInfo.ArticleText = HttpUtility.HtmlEncode(item.Content);
                // This next part is needed because of a change made to the News Articles Provider as of version 
                // 00.06.07.  A change was made so that if there was no summary, just an entry, the entry would be 
                // saved as the content of the post, not as the summary.
                // First, we check to see if we are using a version of NA before this change.
                if (IsPrePageRequired())
                {
                    // Version of NA < 00.06.07
                    articleInfo.Summary = (string.IsNullOrEmpty(item.Summary))
                                            ? HttpUtility.HtmlEncode(item.Content)
                                            : item.Summary;
                }
                else
                {
                    // Version of NA >= 00.06.07
                    articleInfo.Summary = item.Summary;
                }
                if (!string.IsNullOrEmpty(featuredImage)) articleInfo.ImageUrl = featuredImage;

                int tempModuleId = articleInfo.ModuleID;
                ModuleController moduleController = new ModuleController();
                ModuleInfo dnnModuleInfo = moduleController.GetModule(tempModuleId, Null.NullInteger, false);
                settings = PortalSettings.GetTabModuleSettings(new Hashtable(PortalSettings.GetModuleSettings(dnnModuleInfo.ModuleID)), new Hashtable(PortalSettings.GetTabModuleSettings(dnnModuleInfo.TabModuleID)));
                articleSettings = new ArticleSettings(settings, portalSettings, dnnModuleInfo);

                // Process the settings which are saved as categories
                ProcessSettingsFromCategories(item.Categories, ref articleInfo, portalSettings);

                DateTime adjustedPubTime;
                if (item.DateCreated.Year > 1)
                    adjustedPubTime = Common.GetAdjustedServerTime(articleSettings, item.DateCreated.AddMinutes(userInfo.Profile.TimeZone));
                else
                    adjustedPubTime = item.DateCreated;

                DateTime adjustedServerTime = Common.GetAdjustedServerTime(articleSettings, DateTime.Now.ToUniversalTime().AddMinutes(userInfo.Profile.TimeZone));

                // Set the publish date if set in WLW.
                if (adjustedPubTime.Year > 1)
                {

                    if (_setAsEndDate)
                    {
                        articleInfo.EndDate = adjustedPubTime;
                    }
                    else
                    {
                        articleInfo.StartDate = adjustedPubTime;
                    }

                }

                #region Old Method of Handling Time (Left for Reference)
                //                //articleInfo.CreatedDate = DateTime.Now;
                //                // Set the publish date if set in WLW.
                //                if (item.DateCreated.Year > 1)
                //                {
                //                    int tempModuleId = articleInfo.ModuleID;
                //                    ModuleController moduleController = new ModuleController();
                //                    ModuleInfo dnnModuleInfo = moduleController.GetModule(tempModuleId, Null.NullInteger);
                //                    Hashtable settings = PortalSettings.GetTabModuleSettings(new Hashtable(PortalSettings.GetModuleSettings(dnnModuleInfo.ModuleID)), new Hashtable(PortalSettings.GetTabModuleSettings(dnnModuleInfo.TabModuleID)));
                //                    ArticleSettings articleSettings = new ArticleSettings(settings, portalSettings, dnnModuleInfo);
                //                    DateTime startDate = Common.GetAdjustedServerTime(articleSettings, item.DateCreated.ToLocalTime());
                //#if (VentrianEndDate)
                //                    articleInfo.EndDate = startDate;
                //#else
                //                    articleInfo.StartDate = startDate;
                //#endif
                //                }
                # endregion

            }

            // Either way (Page or Post) we'll set the publish and LastUpdate properties.
            articleInfo.IsDraft = !item.Publish;
            articleInfo.LastUpdate = DateTime.Now;
            articleInfo.LastUpdateID = userInfo.UserID;

            // Check to see if this is an approver and they're also trying to publish.  If so, then
            // just publish it.
            if (IsUserPrivileged(userInfo, "AutoApprovalRoles", articleInfo.ModuleID) && item.Publish)
            {
                articleInfo.ApproverID = userInfo.UserID;
                articleInfo.IsApproved = true;
            }

            articleController.UpdateArticle(articleInfo);

            # endregion

            # region Manage Categories
            // Add the Categories if any were selected.  This should only be
            // executed for posts and not for page edits.
            if (item.ItemType == ItemType.Post)
            {
                // first, delete the old Categories before we start
                articleController.DeleteArticleCategories(articleId);

                // next, we load a list of categories that should be excluded (Settings
                // and categorization categories.  
                List<string> categoriesToExclude = GetCategoriesToExclude(portalSettings);

                // Load the default categories
                string defaultCategoryIds = settings["DefaultCategories"] as string;
                List<int> categoriesToAdd = new List<int>();
                if (defaultCategoryIds != null)
                {
                    string[] defaultCatIdArray = defaultCategoryIds.Split(',');
                    foreach (string catId in defaultCatIdArray)
                    {
                        try
                        {
                            categoriesToAdd.Add(Convert.ToInt32(catId));
                        }
                        catch { }
                    }
                }

                // Add the Categories if any were selected
                if ((item.Categories != null && item.Categories.Length > 0) || categoriesToAdd.Count > 0)
                {
                    // Load the list of Categories for this module
                    //MethodInfo mi = categoryContoller.GetType().GetMethod("GetCategoryListAll", new Type[] {typeof(int),typeof(int)});
                    //object[] methodParams = null;
                    //if (mi != null)
                    //{
                    //    methodParams = new object[2];
                    //    methodParams.SetValue(articleModuleId, 0);
                    //    methodParams.SetValue(-1, 1);
                    //}
                    //else
                    //{
                    //    mi = categoryContoller.GetType().GetMethod("GetCategoryList");
                    //    methodParams = new object[1];
                    //    methodParams.SetValue(articleModuleId, 0);
                    //}

                    //ArrayList moduleCategoriesList = (ArrayList)mi.Invoke(categoryContoller, methodParams);
                    ItemCategoryInfo[] moduleCategoriesList = GetCategories(articleInfo.ModuleID, portalSettings);

                    if (item.Categories != null && item.Categories.Length > 0)
                    {
                        foreach (string category in item.Categories)
                        {
                            // Only execute if this isn't one of the exclude categories
                            if (!categoriesToExclude.Contains(category))
                            {
                                // Loop through the category names to get the categoryid for adding to the article.
                                foreach (ItemCategoryInfo categoryInfo in moduleCategoriesList)
                                {
                                    if (categoryInfo.CategoryName == category && !categoriesToAdd.Contains(categoryInfo.CategoryId))
                                    {
                                        categoriesToAdd.Add(categoryInfo.CategoryId);
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    // Now we loop through and add the categories
                    foreach (int catId in categoriesToAdd)
                    {
                        articleController.AddArticleCategory(articleId, catId);
                    }
                }
            }
            # endregion

            if (item.ItemType == ItemType.Post)
            {
                // Check to see if we need to send email messages
                // We only send these if the post is marked for publish and the author does not have 
                // auto approval privileges.
                if (item.Publish
                    && !IsUserPrivileged(userInfo, "AutoApprovalRoles", articleInfo.ModuleID)
                    && settings.Count > 0
                    && articleSettings != null)
                {
                    SendSubmittedForApprovalEmail(settings, articleSettings, articleInfo.ModuleID, articleId,
                                                  -1, portalSettings);
                }
            }

            MetaPostServices.SaveItemImages(articleId, images, ProviderKey, portalSettings, userInfo.UserID);

            // Call a stored procedure which saves the FileId for the Featured Image
            if (!string.IsNullOrEmpty(featuredImage))
            {
                string SQL = "itcmetapost_news_articles_update_imageurl";
                DataProvider.Instance().ExecuteNonQuery(SQL, new object[] { articleId });
            }

            return true;
        }

        /// <summary>
        /// DeleteItem is used to delete either a page or a post.  The difference between the two is noted 
        /// in the itemType parameter which is an enumeration set to either Page or Post.
        /// </summary>
        /// <param name="itemId"></param>
        /// <param name="userInfo"></param>
        /// <param name="portalSettings"></param>
        /// <param name="itemType"></param>
        /// <returns></returns>
        public bool DeleteItem(string itemId, UserInfo userInfo, PortalSettings portalSettings, ItemType itemType)
        {
            int id;
            bool isArticle;
            if (itemId.Length > 3 && itemId.Substring(0, 3) == "aid")
            {
                id = Convert.ToInt32(itemId.Substring(3));
                isArticle = true;
            }
            else
            {
                id = Convert.ToInt32(itemId);
                isArticle = false;
            }

            PageController pageController = new PageController();

            if (itemType == ItemType.Post)
            {
                // For posts, we delete all the associated pages if there are any and then 
                // delete the article.

                ArticleController articleController = new ArticleController();
                int articleId = -1;

                if (isArticle)
                {
                    // ARTICLE
                    articleId = id;
                }
                else
                {
                    // This is a page, so we'll look up the article ID from the page
                    PageInfo pageInfo = pageController.GetPage(id);
                    articleId = pageInfo.ArticleID;
                }

                articleController.DeleteArticle(id);
                articleController.DeleteArticle(articleId);
            }
            else
            {
                pageController.DeletePage(id);
            }


            return true;
        }

        # endregion

        # region Category Related Procedures

        /// <summary>
        /// GetCategories retrieves a list of categories for a particular module.
        /// </summary>
        /// <param name="moduleLevelId"></param>
        /// <param name="userInfo"></param>
        /// <param name="portalSettings"></param>
        /// <returns></returns>		
        public ItemCategoryInfo[] GetCategories(string moduleLevelId, UserInfo userInfo, PortalSettings portalSettings)
        {

            // Get the actual ModuleId since we're tracking the TabModuleId in the moduleLevelId parameter
            DnnModuleInfoStruct moduleInfo = MetaPostServices.GetModuleInfo(Convert.ToInt32(moduleLevelId));
            int articleModuleId = moduleInfo.ModuleId;
            return GetCategories(articleModuleId, portalSettings);
        }

        /// <summary>
        /// GetCategories retrieves a list of categories for a particular module.  This overload was created to allow
        ///		us to re-use the code which builds a list of categories within the GetItemCategories method.
        /// </summary>
        /// <param name="articleModuleId"></param>
        /// <returns></returns>		
        private ItemCategoryInfo[] GetCategories(int articleModuleId, PortalSettings portalSettings)
        {
            ItemCategoryInfo[] categories = null;


            CategoryController categoryController = new CategoryController();

            MethodInfo mi = categoryController.GetType().GetMethod("GetCategoryListAll", new Type[] { typeof(int), typeof(int) });
            object[] methodParams = null;
            if (mi != null)
            {
                methodParams = new object[2];
                methodParams.SetValue(articleModuleId, 0);
                methodParams.SetValue(-1, 1);
            }
            else
            {
                mi = categoryController.GetType().GetMethod("GetCategoryList");
                if (mi.GetParameters().Length == 1)
                {
                    methodParams = new object[1];
                    methodParams.SetValue(articleModuleId, 0);
                }
                else if (mi.GetParameters().Length == 2)
                {
                    methodParams = new object[2];
                    methodParams.SetValue(articleModuleId, 0);
                    methodParams.SetValue(-1, 1);
                }
            }

            ArrayList categoryList = (ArrayList)mi.Invoke(categoryController, methodParams);


            int listCount = (categoryList.Count > 0) ? categoryList.Count + 5 : 4;  //categoryList.Count + 2;  

            categories = new ItemCategoryInfo[listCount];

            // Add the Settings Categories
            ItemCategoryInfo settingsCategory = new ItemCategoryInfo();
            settingsCategory.CategoryId = 2000001;
            // Note that if the value in the second argument changes, please update the GetCategoriesToExclude
            // and ProcessExcludeCategories procedures below.
            settingsCategory.CategoryName = MetaPostServices.GetString("NewsArticlesSettings", "Settings", LocalizationFilePath, portalSettings.DefaultLanguage);
            settingsCategory.Description = settingsCategory.CategoryName;
            settingsCategory.HtmlUrl = "http://www.itcrossing.com/" + settingsCategory.CategoryId.ToString();
            settingsCategory.RssUrl = "http://www.itcrossing.com/" + settingsCategory.CategoryId.ToString();
            categories[0] = settingsCategory;

            settingsCategory = new ItemCategoryInfo();
            settingsCategory.CategoryId = 2000002;
            // Note that if the value in the second argument changes, please update the GetCategoriesToExclude
            // and ProcessExcludeCategories procedures below.
            settingsCategory.CategoryName = MetaPostServices.GetString("Featured", "Featured", LocalizationFilePath, portalSettings.DefaultLanguage);
            settingsCategory.Description = settingsCategory.CategoryName;
            settingsCategory.HtmlUrl = "http://www.itcrossing.com/" + settingsCategory.CategoryId.ToString();
            settingsCategory.RssUrl = "http://www.itcrossing.com/" + settingsCategory.CategoryId.ToString();
            settingsCategory.ParentId = 2000001;
            categories[1] = settingsCategory;

            settingsCategory = new ItemCategoryInfo();
            settingsCategory.CategoryId = 2000003;
            // Note that if the value in the second argument changes, please update the GetCategoriesToExclude
            // and ProcessExcludeCategories procedures below.
            settingsCategory.CategoryName = MetaPostServices.GetString("Secure", "Secure", LocalizationFilePath, portalSettings.DefaultLanguage);
            settingsCategory.Description = settingsCategory.CategoryName;
            settingsCategory.HtmlUrl = "http://www.itcrossing.com/" + settingsCategory.CategoryId.ToString();
            settingsCategory.RssUrl = "http://www.itcrossing.com/" + settingsCategory.CategoryId.ToString();
            settingsCategory.ParentId = 2000001;
            categories[2] = settingsCategory;

            settingsCategory = new ItemCategoryInfo();
            settingsCategory.CategoryId = 2000005;
            // Note that if the value in the second argument changes, please update the GetCategoriesToExclude
            // and ProcessExcludeCategories procedures below.
            settingsCategory.CategoryName = MetaPostServices.GetString("DateAsExpiration", "Date as Expiration", LocalizationFilePath, portalSettings.DefaultLanguage);
            settingsCategory.Description = settingsCategory.CategoryName;
            settingsCategory.HtmlUrl = "http://www.itcrossing.com/" + settingsCategory.CategoryId.ToString();
            settingsCategory.RssUrl = "http://www.itcrossing.com/" + settingsCategory.CategoryId.ToString();
            settingsCategory.ParentId = 2000001;
            categories[3] = settingsCategory;

            if (categoryList.Count > 0)
            {
                // Add the Categories Category
                ItemCategoryInfo categoriesCategory = new ItemCategoryInfo();
                categoriesCategory.CategoryId = 2000004;
                // Note that if the value in the second argument changes, please update the GetCategoriesToExclude
                // and ProcessExcludeCategories procedures below.
                categoriesCategory.CategoryName =
                    MetaPostServices.GetString("NewsArticlesCategories", "Categories",
                                               LocalizationFilePath, portalSettings.DefaultLanguage);
                categoriesCategory.Description = categoriesCategory.CategoryName;
                categoriesCategory.HtmlUrl = "http://www.itcrossing.com/" + settingsCategory.CategoryId.ToString();
                categoriesCategory.RssUrl = "http://www.itcrossing.com/" + settingsCategory.CategoryId.ToString();
                categories[4] = categoriesCategory;


                int i = 5;
                int catCount = 1;
                foreach (CategoryInfo categoryInfo in categoryList)
                {
                    ItemCategoryInfo category = new ItemCategoryInfo();

                    //First Category
                    category.CategoryId = categoryInfo.CategoryID;
                    category.HtmlUrl = "http://www.itcrossing.com/" + settingsCategory.CategoryId.ToString();
                    category.RssUrl = "http://www.itcrossing.com/" + settingsCategory.CategoryId.ToString();
                    category.Description = string.Format("({0}) ", catCount.ToString()) + categoryInfo.Name;
                    category.CategoryName = string.Format("({0}) ", catCount.ToString()) + categoryInfo.Name;
                    // Use reflection to see if a ParentID property is supported
                    int parentId = -1;
                    PropertyInfo pi = categoryInfo.GetType().GetProperty("ParentID");
                    if (pi != null)
                    {
                        parentId = Convert.ToInt32(pi.GetValue(categoryInfo, null));
                    }
                    category.ParentId = (parentId == -1) ? 2000004 : parentId;
                    categories[i] = category;
                    i++;
                    catCount++;
                }
            }

            return categories;
        }

        public bool SetItemCategories(string itemId, ItemCategory[] itemCategories, UserInfo userInfo,
                                PortalSettings portalSettings)
        {
            // Not imlemented
            return true;
        }

        public ItemCategory[] GetItemCategories(string itemId, UserInfo userInfo,
                                                PortalSettings portalSettings)
        {
            List<ItemCategory> itemCategories = new List<ItemCategory>();

            int articleId = 0;
            if (itemId.StartsWith("aid"))
            {
                articleId = Convert.ToInt32(itemId.Substring(3));
            }
            // Get the Categories for the article
            ArticleController articleController = new ArticleController();
            ArticleInfo articleInfo = articleController.GetArticle(articleId);
            ArrayList cats = articleController.GetArticleCategories(articleId);
            ItemCategoryInfo[] moduleCategories = GetCategories(articleInfo.ModuleID, portalSettings);
            List<string> catValues = new List<string>();

            foreach (CategoryInfo categoryInfo in cats)
            {
                // We're looping through the categories for the article and for each one
                // we'll need to find the corresponding category in the overall list of categories.
                // This will ensure that any numbering or formatting added to the categories
                // is reflected in the categories we add to the item.
                foreach (ItemCategoryInfo ci in moduleCategories)
                {
                    if (ci.CategoryId == categoryInfo.CategoryID)
                    {
                        ItemCategory ic = new ItemCategory();
                        ic.CategoryId = categoryInfo.CategoryID.ToString();
                        ic.CategoryName = ci.CategoryName;
                        itemCategories.Add(ic);
                        break;
                    }
                }
            }

            // Check to see if any of the settings categories need to be added:
            if (articleInfo.IsFeatured)
            {
                ItemCategory ic = new ItemCategory();
                ic.CategoryId = "2000002";
                ic.CategoryName = MetaPostServices.GetString("Featured", "Featured", LocalizationFilePath, portalSettings.DefaultLanguage);
                itemCategories.Add(ic);
            }
            if (articleInfo.IsSecure)
            {
                ItemCategory ic = new ItemCategory();
                ic.CategoryId = "2000003";
                ic.CategoryName = MetaPostServices.GetString("Secure", "Secure", LocalizationFilePath, portalSettings.DefaultLanguage);
                itemCategories.Add(ic);
            }

            return itemCategories.ToArray();
        }

        /// <summary>
        /// Used to add a new category for modules which support this feature.
        /// </summary>
        /// <param name="moduleLevelId"></param>
        /// <param name="userInfo"></param>
        /// <param name="portalSettings"></param>
        /// <returns></returns>
        public int NewCategory(string moduleLevelId, NewItemCategory category, UserInfo userInfo, PortalSettings portalSettings)
        {
            int categoryId = 0;
            // Check first to see if the user should have the ability to add Categories

            ModuleController moduleController = new ModuleController();
            //ModuleInfo moduleInfo =
            //    moduleController.getm(portalSettings.PortalId, moduleName);
            //bool bHasAdminRights = PortalSecurity.IsInRoles(_moduleConfiguration.AuthorizedEditRoles);
            //Dim blnHasModuleEditPermissions As Boolean = PortalSecurity.IsInRoles(_moduleConfiguration.AuthorizedEditRoles)
            throw new MetaPostException("FeatureNotImplemented",
                                        "This feature is currently not implemented.");

            return categoryId;
        }

        # endregion

        #endregion

        # region Private Procedures Specific to Ventrian News Articles Module

        private void ExtractSummaryFromExtendedContent(ref Item item)
        {
            if (!string.IsNullOrEmpty(item.ExtendedContent))
            {
                // Extract the summary from the excerpt
                item.Summary = item.Content;
                item.Content = item.ExtendedContent;
            }
            // Also check to see if there is only one part to the content (no summary and 
            // no extended content).  In this case, we'll just submit a summary.
            if (string.IsNullOrEmpty(item.Summary) && string.IsNullOrEmpty(item.ExtendedContent))
            {

                if (IsPrePageRequired()) // Version of NA < 00.06.07
                {
                    item.Summary = item.Content;
                    item.Content = "";
                }

            }
        }

        private void FindAndPlaceSummary(ref Item item)
        {
            if (!string.IsNullOrEmpty(item.Summary)
                && !string.IsNullOrEmpty(item.Content)) //MetaPostServices.ContainsHtml(item.Summary)
            {
                // We have HTML, so put the HTML in the content above a <!--more--> tag
                item.Content = item.Summary + "<!--more-->" + item.Content;
                item.Summary = "";
            }
            else if (!string.IsNullOrEmpty(item.Summary) && string.IsNullOrEmpty(item.Content))
            {
                item.Content = item.Summary;
                item.Summary = string.Empty;
            }

        }

        private List<string> GetCategoriesToExclude(PortalSettings portalSettings)
        {
            List<string> categoriesToExclude = new List<string>();
            categoriesToExclude.Add(MetaPostServices.GetString("NewsArticlesSettings", "Settings", LocalizationFilePath, portalSettings.DefaultLanguage));
            categoriesToExclude.Add(MetaPostServices.GetString("Featured", "Featured", LocalizationFilePath, portalSettings.DefaultLanguage));
            categoriesToExclude.Add(MetaPostServices.GetString("Secure", "Secure", LocalizationFilePath, portalSettings.DefaultLanguage));
            categoriesToExclude.Add(MetaPostServices.GetString("DateAsExpiration", "Date as Expiration", LocalizationFilePath, portalSettings.DefaultLanguage));
            categoriesToExclude.Add(MetaPostServices.GetString("NewsArticlesCategories", "Categories", LocalizationFilePath, portalSettings.DefaultLanguage));

            return categoriesToExclude;
        }

        private void ProcessSettingsFromCategories(string[] categories, ref ArticleInfo articleInfo, PortalSettings portalSettings)
        {
            articleInfo.IsFeatured = false;
            articleInfo.IsSecure = false;
            if (categories != null)
            {
                foreach (string category in categories)
                {
                    if (category == MetaPostServices.GetString("Featured", "Featured", LocalizationFilePath, portalSettings.DefaultLanguage))
                    {
                        articleInfo.IsFeatured = true;
                    }
                    if (category == MetaPostServices.GetString("Secure", "Secure", LocalizationFilePath, portalSettings.DefaultLanguage))
                    {
                        articleInfo.IsSecure = true;
                    }
                    if (category == MetaPostServices.GetString("DateAsExpiration", "Date as Expiration", LocalizationFilePath, portalSettings.DefaultLanguage))
                    {
                        _setAsEndDate = true;
                    }
                }
            }
        }

        private bool IsUserPrivileged(UserInfo userInfo, string roleGroup, int moduleLevelId)
        {
            if (userInfo.IsSuperUser)
            {
                return true;
            }
            if (IsAdmin(userInfo.Roles))
            {
                return true;
            }
            Hashtable moduleSettings = PortalSettings.GetModuleSettings(moduleLevelId);
            if (moduleSettings.Contains(roleGroup))
            {
                string roles = moduleSettings[roleGroup].ToString();
                string[] rolesArray = roles.Split(';');
                foreach (string role in rolesArray)
                {
                    if (userInfo.IsInRole(role))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        private bool IsAdmin(string[] roles)
        {
            foreach (string role in roles)
            {
                if (role == "Administrators" || role.ToLower() == "mp_na_editor") return true;
            }
            return false;
        }

        private string GetFeaturedImage(ref Item item)
        {

            // Regex for stripping off the anchor tag for ai images.
            // this new approach returns the href of the anchor tag surrounding the image tag.
            string regex = @"<a[^>]+?(?:png|jpg|jpeg|gif)[^>]+?>[^<]*?<img[^>]+?alt=""ai""[^>]+>[^<]*?</a>";
            // Old approach (left for reference) used the src of the image tag
            //string regex = "<img[^>]+?alt=\"ai\".*?>";
            RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Singleline;

            string imageUrl = GetFeaturedImageUrl(item.Summary, regex);
            if (!string.IsNullOrEmpty(imageUrl))
            {
                // Remove the image from the Summary
                item.Summary = Regex.Replace(item.Summary, regex, string.Empty, options);
                return imageUrl;
            }

            // Now, it's possible that the image was actually in the content.  So, we'll 
            // check there if the imageUrl is empty.
            imageUrl = GetFeaturedImageUrl(item.Content, regex);
            if (!string.IsNullOrEmpty(imageUrl))
            {
                // Remove the image from the Content
                item.Content = Regex.Replace(item.Content, regex, string.Empty, options);
                return imageUrl;
            }

            // Finally, it's also possible that the image was placed in the extended 
            // content.  We'll check here if the imageUrl is still empty.
            imageUrl = GetFeaturedImageUrl(item.ExtendedContent, regex);
            if (!string.IsNullOrEmpty(imageUrl))
            {
                // Remove the image from the Content
                item.ExtendedContent = Regex.Replace(item.ExtendedContent, regex, string.Empty, options);
                return imageUrl;
            }

            return imageUrl;

        }

        private string GetFeaturedImageUrl(string input, string regex)
        {
            string imageUrl = string.Empty;

            if (!string.IsNullOrEmpty(input))
            {
                RegexOptions options = RegexOptions.Singleline | RegexOptions.IgnoreCase;

                MatchCollection matches = Regex.Matches(input, regex, options);
                foreach (Match match in matches)
                {
                    // extract the src attribute from the image
                    // actually, now we've changed this to extract the href of the 
                    // anchor that surrounds the image.
                    string regexSrc = "href=\"(?<src>[^\"]+?)\"";
                    input = match.Value;
                    Match src = Regex.Match(input, regexSrc, options);
                    if (src != null && src.Groups["src"].Captures.Count > 0)
                    {
                        // We have an image Url
                        imageUrl = src.Groups["src"].Captures[0].Value;
                        break;
                    }
                }
            }

            return imageUrl;
        }

        private void ShowFeaturedImage(ref Item item, string imageUrl)
        {
            if (!string.IsNullOrEmpty(imageUrl) && !imageUrl.StartsWith("FileID"))
            {
                string imageTag = "<img src=\"" + imageUrl + "\" alt=\"ai\" />";
                item.Content = imageTag + item.Content;
            }
        }

        private int GetNextPageOrder(string itemPageOrder, int articleId)
        {
            PageController pageController = new PageController();
            int pageOrder;
            if (itemPageOrder == "0" || string.IsNullOrEmpty(itemPageOrder))
            {
                ArrayList articlePages = pageController.GetPageList(articleId);
                int maxSortOrder = -1;
                foreach (PageInfo page in articlePages)
                {
                    maxSortOrder = (page.SortOrder > maxSortOrder) ? page.SortOrder : maxSortOrder;
                }
                pageOrder = maxSortOrder + 1;
            }
            else
            {
                try
                {
                    pageOrder = Convert.ToInt32(itemPageOrder);
                }
                catch
                {
                    pageOrder = 0;
                }
            }
            return pageOrder;
        }

        private bool IsPrePageRequired()
        {
            // This procedure is needed because of a change made to the News Articles Provider as of version 
            // 00.06.07.  A change was made so that if there was no summary, just an entry, the entry would be 
            // saved as the content of the post, not as the summary.
            // First, we check to see if we are using a version of NA before this change.

            string sql =
                    "SELECT Version FROM {databaseOwner}{objectQualifier}DesktopModules (nolock) WHERE (Version < '00.06.07') AND (FolderName = 'DnnForge - LatestArticles')";
            using (IDataReader dr = DataProvider.Instance().ExecuteSQL(sql))
            {
                return dr.Read();
            }
        }

        private void SendSubmittedForApprovalEmail(Hashtable settings, ArticleSettings articleSettings, int moduleId, int articleId, int tabId, PortalSettings portalSettings)
        {
            ArticleController articleController = new ArticleController();
            ArticleInfo article = articleController.GetArticle(articleId);
            TabController tabController = new TabController();
            if (tabId == -1)
            {
                // We need to get a tabid from the ModuleId
                // This will also retrieve a TabId that is passed in through the querystring.
                tabId = GetTabId(moduleId, portalSettings);
            }
            TabInfo tabInfo = tabController.GetTab(tabId, portalSettings.PortalId, false);
            if (settings.Contains(ArticleConstants.NOTIFY_SUBMISSION_SETTING))
            {
                if (Convert.ToBoolean(settings[ArticleConstants.NOTIFY_SUBMISSION_SETTING]) == true)
                {
                    EmailTemplateController emailTemplateController = new EmailTemplateController();
                    string emails = emailTemplateController.GetApproverDistributionList(moduleId);
                    foreach (string email in emails.Split(';'))
                    {
                        if (email != String.Empty)
                        {
                            string link = Common.GetArticleLink(article, tabInfo, articleSettings, false);
                            SendFormattedEmail(moduleId, link, article, email, EmailTemplateType.ArticleSubmission, articleSettings);
                        }
                    }
                }
            }
            if (settings.Contains(ArticleConstants.NOTIFY_SUBMISSION_SETTING_EMAIL))
            {
                if (settings[ArticleConstants.NOTIFY_SUBMISSION_SETTING_EMAIL].ToString() != string.Empty)
                {
                    string link = Common.GetArticleLink(article, tabInfo, articleSettings, false);
                    SendFormattedEmail(moduleId, link, article, settings[ArticleConstants.NOTIFY_SUBMISSION_SETTING_EMAIL].ToString(), EmailTemplateType.ArticleSubmission, articleSettings);
                }
            }
        }

        private static void SendFormattedEmail(int moduleId, string link, ArticleInfo article, string email, EmailTemplateType templateType, ArticleSettings articleSettings)
        {
            EmailTemplateController emailTemplateController = new EmailTemplateController();
            Type[] oldTypes = new Type[] { typeof(int), typeof(string), typeof(ArticleInfo), typeof(EmailTemplateType), typeof(string) };
            MethodInfo miSendFormattedEmail = emailTemplateController.GetType().GetMethod("SendFormattedEmail", oldTypes);
            if (miSendFormattedEmail == null)
            {
                // new interface is being used (NA 7.50 or greater)
                Type[] newTypes = new Type[] { typeof(int), typeof(string), typeof(ArticleInfo), typeof(EmailTemplateType), typeof(string), typeof(ArticleSettings) };
                miSendFormattedEmail = emailTemplateController.GetType().GetMethod("SendFormattedEmail", newTypes);
            }
            ParameterInfo[] piParameters = miSendFormattedEmail.GetParameters();
            if (piParameters.Length == 6)
            {
                object[] methodParams = new object[6];
                methodParams.SetValue(moduleId, 0);
                methodParams.SetValue(link, 1);
                methodParams.SetValue(article, 2);
                methodParams.SetValue(templateType, 3);
                methodParams.SetValue(email, 4);
                methodParams.SetValue(articleSettings, 5);
                miSendFormattedEmail.Invoke(emailTemplateController, methodParams);
            }
            else
            {
                object[] methodParams = new object[5];
                methodParams.SetValue(moduleId, 0);
                methodParams.SetValue(link, 1);
                methodParams.SetValue(article, 2);
                methodParams.SetValue(templateType, 3);
                methodParams.SetValue(email, 4);
                methodParams.SetValue(articleSettings, 5);
                miSendFormattedEmail.Invoke(emailTemplateController, methodParams);
            }

            //emailTemplateController.SendFormattedEmail(moduleId, Common.GetArticleLink(article, tabInfo, articleSettings, false), article, EmailTemplateType.ArticleSubmission, email);
            //emailTemplateController.SendFormattedEmail(moduleId, Common.GetArticleLink(article, tabInfo, articleSettings, false), article, EmailTemplateType.ArticleSubmission, email, articleSettings);

        }

        private int GetTabId(int moduleId, PortalSettings portalSettings)
        {
            int retValue = 0;

            if (HttpContext.Current.Request["tabid"] != null && HttpContext.Current.Request["tabid"].ToString() != string.Empty)
            {
                try
                {
                    retValue = Convert.ToInt32(HttpContext.Current.Request["tabid"].ToString());
                }
                catch { }
            }

            if (retValue == 0)
            {
                List<DnnModuleInfoStruct> dnnModuleInfos = MetaPostServices.GetModuleInfos(ProviderKey, portalSettings.PortalId);
                foreach (DnnModuleInfoStruct modInfo in dnnModuleInfos)
                {
                    if (modInfo.ModuleId == moduleId)
                    {
                        retValue = modInfo.TabId;
                    }
                }
            }

            return retValue;
        }

        # endregion

    }
}
